package com.xg.java.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

@Component
@Slf4j
public class TimeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("在请求处理之前进行调用（Controller方法调用之前）");
        request.setAttribute("startTime", System.currentTimeMillis());
//        HandlerMethod handlerMethod = (HandlerMethod) handler;
//        log.info("controller object is {}", handlerMethod.getBean().getClass().getName());
//        log.info("controller method is {}", handlerMethod.getMethod());
        //添加跨域CORS
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type,token");
        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
        response.setHeader("Access-Control-Allow-Origin", "CORS");
        // 需要返回true，否则请求不会被控制器处理
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.debug("请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后），如果异常发生，则该方法不会被调用");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.debug("在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）");
        long startTime = (long) request.getAttribute("startTime");
        log.debug("time consume is {}", System.currentTimeMillis() - startTime);
    }
    //与过滤器不同的是，拦截器使用@Component修饰后，在SpringBoot中还需要通过实现WebMvcConfigurer手动注册：

    // java配置类
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Autowired
        private TimeInterceptor timeInterceptor;

        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(timeInterceptor);
        }
    }

//    如果是在SpringMVC中，则需要通过xml文件配置<mvc:interceptors>节点信息。
//
//    切片Aspect
//            切片概述
//    相比过滤器，拦截器能够知道用户发出的请求最终被哪个控制器处理，但是拦截器还有一个明显的不足，即不能够获取request的参数以及控制器处理之后的response。所以就有了切片的用武之地了。
//
//    切片实现
//    切片的实现需要注意@Aspect,@Component以及@Around这三个注解的使用，详细查看官方文档：
//
//    docs.spring.io/spring/docs/5.0.12.RELEASE/spring-framework-reference/core.html#aop

    @Aspect
    @Component
    public class TimeAspect {

        @Around("execution(* me.ifight.controller.*.*(..))")
        public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            log.info("切片开始。。。");
            long startTime = System.currentTimeMillis();

            // 获取请求入参
            Object[] args = proceedingJoinPoint.getArgs();
            Arrays.stream(args).forEach(arg -> log.info("arg is {}", arg));

            // 获取相应
            Object response = proceedingJoinPoint.proceed();

            long endTime = System.currentTimeMillis();
            log.info("请求:{}, 耗时{}ms", proceedingJoinPoint.getSignature(), (endTime - startTime));
            log.info("切片结束。。。");
            return null;
        }
    }
}
